﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UtilZ.Dotnet.Ex.Base;

namespace UtilZ.Dotnet.Ex.Model
{
    /// <summary>
    /// 特定类型值域
    /// </summary>
    public class TypeValueRange
    {
        private readonly static Type _enumRangeAttributeType = null;
        private readonly static Type _valueRangeAttributeType = null;
        private readonly static Type _customRangeAttributeType = null;
        static TypeValueRange()
        {
            _enumRangeAttributeType = typeof(EnumRangeAttribute);
            _valueRangeAttributeType = typeof(ValueRangeAttribute);
            _customRangeAttributeType = typeof(CustomRangeAttribute);
        }


        private readonly Type _type;
        /// <summary>
        /// 获取特定类型
        /// </summary>
        public Type Type
        {
            get { return _type; }
        }


        /// <summary>
        /// 附加数据
        /// </summary>
        public object Tag { get; set; }


        private readonly Dictionary<string, IValueRange> _valueRangeDic;




        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="type">特定类型</param>
        /// <param name="valueRangeDic">默认值域字典集合</param>
        /// <exception cref="ArgumentNullException">特定类型为null异常</exception>
        public TypeValueRange(Type type, Dictionary<string, IValueRange> valueRangeDic)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            if (valueRangeDic == null)
            {
                valueRangeDic = new Dictionary<string, IValueRange>();
            }

            this._type = type;
            this._valueRangeDic = new Dictionary<string, IValueRange>(valueRangeDic);
        }

        /// <summary>
        /// 构造函数,使用特定类型初始化
        /// </summary>
        /// <param name="type">特定类型</param>
        /// <exception cref="ArgumentNullException">特定类型为null异常</exception>
        /// <exception cref="ArgumentException">属性参数值范围错误异常</exception>
        /// <exception cref="NotSupportedException">不支持的属性类型异常</exception>
        public TypeValueRange(Type type)
        {
            if (type == null)
            {
                throw new ArgumentNullException(nameof(type));
            }

            IValueRange valueRange;
            var valueRangeDic = new Dictionary<string, IValueRange>();

            PropertyInfo[] proInfos = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (var proInfo in proInfos)
            {
                valueRange = CreatePropertyValueRange(proInfo);
                if (valueRange != null)
                {
                    valueRangeDic.Add(proInfo.Name, valueRange);
                }
            }

            FieldInfo[] fieldInfos = type.GetFields(BindingFlags.Public | BindingFlags.Instance);
            foreach (var fieldInfo in fieldInfos)
            {
                valueRange = CreateFieldValueRange(fieldInfo);
                if (valueRange != null)
                {
                    valueRangeDic.Add(fieldInfo.Name, valueRange);
                }
            }

            this._type = type;
            this._valueRangeDic = valueRangeDic;
        }

        /// <summary>
        /// 创建属性值范围对象
        /// </summary>
        /// <param name="proInfo">属性信息</param>
        /// <returns></returns>
        public static IValueRange CreatePropertyValueRange(PropertyInfo proInfo)
        {
            IValueRange valueRange;
            Attribute att = proInfo.GetCustomAttribute(_enumRangeAttributeType, true);
            if (att != null)
            {
                valueRange = CreateValueRangeByEnumRangeAttribute(proInfo.Name, (EnumRangeAttribute)att);
                return valueRange;
            }

            att = proInfo.GetCustomAttribute(_valueRangeAttributeType, true);
            if (att != null)
            {
                valueRange = CreateValueRangeByValueRangeAttribute(proInfo.Name, (ValueRangeAttribute)att, proInfo.DeclaringType, proInfo.PropertyType, "属性", proInfo.Name);
                return valueRange;
            }

            att = proInfo.GetCustomAttribute(_customRangeAttributeType, true);
            if (att != null)
            {
                valueRange = (IValueRange)Activator.CreateInstance(((CustomRangeAttribute)att).Type);
                return valueRange;
            }

            return null;
        }

        /// <summary>
        /// 创建字段值范围对象
        /// </summary>
        /// <param name="fieldInfo">字段信息</param>
        /// <returns></returns>
        public static IValueRange CreateFieldValueRange(FieldInfo fieldInfo)
        {
            IValueRange valueRange;
            Attribute att = fieldInfo.GetCustomAttribute(_enumRangeAttributeType, true);
            if (att != null)
            {
                valueRange = CreateValueRangeByEnumRangeAttribute(fieldInfo.Name, (EnumRangeAttribute)att);
                return valueRange;
            }

            att = fieldInfo.GetCustomAttribute(_valueRangeAttributeType, true);
            if (att != null)
            {
                valueRange = CreateValueRangeByValueRangeAttribute(fieldInfo.Name, (ValueRangeAttribute)att, fieldInfo.DeclaringType, fieldInfo.FieldType, "字段", fieldInfo.Name);
                return valueRange;
            }

            att = fieldInfo.GetCustomAttribute(_customRangeAttributeType, true);
            if (att != null)
            {
                valueRange = (IValueRange)Activator.CreateInstance(((CustomRangeAttribute)att).Type);
                return valueRange;
            }

            return null;
        }

        private static IValueRange CreateValueRangeByEnumRangeAttribute(string proFieldName, EnumRangeAttribute valueRangeAtt)
        {
            Type eleType = valueRangeAtt.Values.ElementAt(0).GetType();
            IValueRange valueRange;
            switch (Type.GetTypeCode(TypeEx.GetPrimitiveType(eleType)))
            {
                case TypeCode.Char:
                    valueRange = new EnumValueRange<char>(proFieldName, valueRangeAtt.Values.Select(t => { return (char)t; }));
                    break;
                case TypeCode.SByte:
                    valueRange = new EnumValueRange<sbyte>(proFieldName, valueRangeAtt.Values.Select(t => { return (sbyte)t; }));
                    break;
                case TypeCode.Byte:
                    valueRange = new EnumValueRange<byte>(proFieldName, valueRangeAtt.Values.Select(t => { return (byte)t; }));
                    break;
                case TypeCode.UInt16:
                    valueRange = new EnumValueRange<UInt16>(proFieldName, valueRangeAtt.Values.Select(t => { return (UInt16)t; }));
                    break;
                case TypeCode.Int16:
                    valueRange = new EnumValueRange<Int16>(proFieldName, valueRangeAtt.Values.Select(t => { return (Int16)t; }));
                    break;
                case TypeCode.UInt32:
                    valueRange = new EnumValueRange<UInt32>(proFieldName, valueRangeAtt.Values.Select(t => { return (UInt32)t; }));
                    break;
                case TypeCode.Int32:
                    valueRange = new EnumValueRange<Int32>(proFieldName, valueRangeAtt.Values.Select(t => { return (Int32)t; }));
                    break;
                case TypeCode.UInt64:
                    valueRange = new EnumValueRange<UInt64>(proFieldName, valueRangeAtt.Values.Select(t => { return (UInt64)t; }));
                    break;
                case TypeCode.Int64:
                    valueRange = new EnumValueRange<Int64>(proFieldName, valueRangeAtt.Values.Select(t => { return (Int64)t; }));
                    break;
                case TypeCode.Single:
                    valueRange = new EnumValueRange<float>(proFieldName, valueRangeAtt.Values.Select(t => { return (float)t; }));
                    break;
                case TypeCode.Double:
                    valueRange = new EnumValueRange<double>(proFieldName, valueRangeAtt.Values.Select(t => { return (double)t; }));
                    break;
                case TypeCode.Decimal:
                    valueRange = new EnumValueRange<decimal>(proFieldName, valueRangeAtt.Values.Select(t => { return (decimal)t; }));
                    break;
                case TypeCode.DateTime:
                    valueRange = new EnumValueRange<DateTime>(proFieldName, valueRangeAtt.Values.Select(t => { return (DateTime)t; }));
                    break;
                case TypeCode.Boolean:
                    valueRange = new EnumValueRange<bool>(proFieldName, valueRangeAtt.Values.Select(t => { return (bool)t; }));
                    break;
                case TypeCode.String:
                    valueRange = new EnumValueRange<string>(proFieldName, valueRangeAtt.Values.Select(t => { return (string)t; }));
                    break;
                case TypeCode.Object:
                    valueRange = new EnumValueRange<object>(proFieldName, valueRangeAtt.Values);
                    break;
                default:
                    throw new NotSupportedException($"不支持的类型{eleType.FullName}");
            }

            return valueRange;
        }

        private static IValueRange CreateValueRangeByValueRangeAttribute(string name, ValueRangeAttribute valueRangeAtt, Type ownerType, Type memType, string memTypeName, string memName)
        {
            IValueRange valueRange;
            switch (Type.GetTypeCode(TypeEx.GetPrimitiveType(memType)))
            {
                case TypeCode.Char:
                    Char chMin = Convert.ToChar(valueRangeAtt.Min);
                    Char chMax = Convert.ToChar(valueRangeAtt.Max);
                    if (chMin > chMax)
                    {
                        throw new ArgumentException($"{memTypeName}{ownerType.FullName}.{memName}上\"{nameof(ValueRangeAttribute)}\"特性标注的参数值范围最小值大于了最大值");
                    }
                    valueRange = new NumberValueRange<char>(name, chMin, chMax, valueRangeAtt.RangeType);
                    break;
                case TypeCode.SByte:
                    SByte sbyteMin = Convert.ToSByte(valueRangeAtt.Min);
                    SByte sbyteMax = Convert.ToSByte(valueRangeAtt.Max);
                    if (sbyteMin > sbyteMax)
                    {
                        throw new ArgumentException($"{memTypeName}{ownerType.FullName}.{memName}上\"{nameof(ValueRangeAttribute)}\"特性标注的参数值范围最小值大于了最大值");
                    }
                    valueRange = new NumberValueRange<SByte>(name, sbyteMin, sbyteMax, valueRangeAtt.RangeType);
                    break;
                case TypeCode.Byte:
                    Byte byteMin = Convert.ToByte(valueRangeAtt.Min);
                    Byte byteMax = Convert.ToByte(valueRangeAtt.Max);
                    if (byteMin > byteMax)
                    {
                        throw new ArgumentException($"{memTypeName}{ownerType.FullName}.{memName}上\"{nameof(ValueRangeAttribute)}\"特性标注的参数值范围最小值大于了最大值");
                    }
                    valueRange = new NumberValueRange<Byte>(name, byteMin, byteMax, valueRangeAtt.RangeType);
                    break;
                case TypeCode.UInt16:
                    UInt16 uint16Min = Convert.ToUInt16(valueRangeAtt.Min);
                    UInt16 uint16Max = Convert.ToUInt16(valueRangeAtt.Max);
                    if (uint16Min > uint16Max)
                    {
                        throw new ArgumentException($"{memTypeName}{ownerType.FullName}.{memName}上\"{nameof(ValueRangeAttribute)}\"特性标注的参数值范围最小值大于了最大值");
                    }
                    valueRange = new NumberValueRange<UInt16>(name, uint16Min, uint16Max, valueRangeAtt.RangeType);
                    break;
                case TypeCode.Int16:
                    Int16 int16Min = Convert.ToInt16(valueRangeAtt.Min);
                    Int16 int16Max = Convert.ToInt16(valueRangeAtt.Max);
                    if (int16Min > int16Max)
                    {
                        throw new ArgumentException($"{memTypeName}{ownerType.FullName}.{memName}上\"{nameof(ValueRangeAttribute)}\"特性标注的参数值范围最小值大于了最大值");
                    }
                    valueRange = new NumberValueRange<Int16>(name, int16Min, int16Max, valueRangeAtt.RangeType);
                    break;
                case TypeCode.UInt32:
                    UInt32 uint32Min = Convert.ToUInt32(valueRangeAtt.Min);
                    UInt32 uint32Max = Convert.ToUInt32(valueRangeAtt.Max);
                    if (uint32Min > uint32Max)
                    {
                        throw new ArgumentException($"{memTypeName}{ownerType.FullName}.{memName}上\"{nameof(ValueRangeAttribute)}\"特性标注的参数值范围最小值大于了最大值");
                    }
                    valueRange = new NumberValueRange<UInt32>(name, uint32Min, uint32Max, valueRangeAtt.RangeType);
                    break;
                case TypeCode.Int32:
                    Int32 int32Min = Convert.ToInt32(valueRangeAtt.Min);
                    Int32 int32Max = Convert.ToInt32(valueRangeAtt.Max);
                    if (int32Min > int32Max)
                    {
                        throw new ArgumentException($"{memTypeName}{ownerType.FullName}.{memName}上\"{nameof(ValueRangeAttribute)}\"特性标注的参数值范围最小值大于了最大值");
                    }
                    valueRange = new NumberValueRange<Int32>(name, int32Min, int32Max, valueRangeAtt.RangeType);
                    break;
                case TypeCode.UInt64:
                    UInt64 uint64Min = Convert.ToUInt64(valueRangeAtt.Min);
                    UInt64 uint64Max = Convert.ToUInt64(valueRangeAtt.Max);
                    if (uint64Min > uint64Max)
                    {
                        throw new ArgumentException($"{memTypeName}{ownerType.FullName}.{memName}上\"{nameof(ValueRangeAttribute)}\"特性标注的参数值范围最小值大于了最大值");
                    }
                    valueRange = new NumberValueRange<UInt64>(name, uint64Min, uint64Max, valueRangeAtt.RangeType);
                    break;
                case TypeCode.Int64:
                    Int64 int64Min = Convert.ToInt64(valueRangeAtt.Min);
                    Int64 int64Max = Convert.ToInt64(valueRangeAtt.Max);
                    if (int64Min > int64Max)
                    {
                        throw new ArgumentException($"{memTypeName}{ownerType.FullName}.{memName}上\"{nameof(ValueRangeAttribute)}\"特性标注的参数值范围最小值大于了最大值");
                    }
                    valueRange = new NumberValueRange<Int64>(name, int64Min, int64Max, valueRangeAtt.RangeType);
                    break;
                case TypeCode.Single:
                    Single floatMin = Convert.ToSingle(valueRangeAtt.Min);
                    Single floatMax = Convert.ToSingle(valueRangeAtt.Max);
                    if (floatMin > floatMax)
                    {
                        throw new ArgumentException($"{memTypeName}{ownerType.FullName}.{memName}上\"{nameof(ValueRangeAttribute)}\"特性标注的参数值范围最小值大于了最大值");
                    }
                    valueRange = new NumberValueRange<Single>(name, floatMin, floatMax, valueRangeAtt.RangeType);
                    break;
                case TypeCode.Double:
                    Double doubleMin = Convert.ToDouble(valueRangeAtt.Min);
                    Double doubleMax = Convert.ToDouble(valueRangeAtt.Max);
                    if (doubleMin > doubleMax)
                    {
                        throw new ArgumentException($"{memTypeName}{ownerType.FullName}.{memName}上\"{nameof(ValueRangeAttribute)}\"特性标注的参数值范围最小值大于了最大值");
                    }
                    valueRange = new NumberValueRange<Double>(name, doubleMin, doubleMax, valueRangeAtt.RangeType);
                    break;
                case TypeCode.Decimal:
                    Decimal decimalMin = Convert.ToDecimal(valueRangeAtt.Min);
                    Decimal decimalMax = Convert.ToDecimal(valueRangeAtt.Max);
                    if (decimalMin > decimalMax)
                    {
                        throw new ArgumentException($"{memTypeName}{ownerType.FullName}.{memName}上\"{nameof(ValueRangeAttribute)}\"特性标注的参数值范围最小值大于了最大值");
                    }
                    valueRange = new NumberValueRange<Decimal>(name, decimalMin, decimalMax, valueRangeAtt.RangeType);
                    break;
                default:
                    throw new NotSupportedException($"不支持的类型{memType.FullName}");
            }

            return valueRange;
        }




        /// <summary>
        /// 设置成员值域
        /// </summary>
        /// <param name="memberName">属性或字段名称</param>
        /// <param name="valueRange">值域</param>
        /// <exception cref="ArgumentNullException">成员名称为null或空或者值域为null将抛出异常</exception>
        public void SetValueRange(string memberName, IValueRange valueRange)
        {
            if (string.IsNullOrEmpty(memberName))
            {
                throw new ArgumentNullException(nameof(memberName));
            }

            if (this._type.GetProperty(memberName) == null && this._type.GetField(memberName) == null)
            {
                throw new ArgumentException($"类型{this._type.FullName}中未定义名为\"{memberName}\"的属性或字段");
            }

            this._valueRangeDic[memberName] = valueRange;
        }

        /// <summary>
        /// 添加一个值域
        /// </summary>
        /// <param name="memberName">属性或字段名称</param>
        /// <param name="valueRange">值域</param>
        /// <exception cref="ArgumentNullException">成员名称为null或空或者值域为null将抛出异常</exception>
        public void AddValueRange(string memberName, IValueRange valueRange)
        {
            if (string.IsNullOrEmpty(memberName))
            {
                throw new ArgumentNullException(nameof(memberName));
            }

            if (this._type.GetProperty(memberName) == null && this._type.GetField(memberName) == null)
            {
                throw new ArgumentException($"类型{this._type.FullName}中未定义名为\"{memberName}\"的属性或字段");
            }

            if (this._valueRangeDic.ContainsKey(memberName))
            {
                throw new ArgumentException($"{this._type.FullName}.{memberName}值域范围已添加,不可重复添加");
            }

            this._valueRangeDic.Add(memberName, valueRange);
        }

        /// <summary>
        /// 添加一个值域
        /// </summary>
        /// <param name="valueRangeDic">值域字典集合</param>
        public void AddValueRange(IDictionary<string, IValueRange> valueRangeDic)
        {
            if (valueRangeDic == null || valueRangeDic.Count == 0)
            {
                return;
            }

            foreach (var kv in valueRangeDic)
            {
                if (this._valueRangeDic.ContainsKey(kv.Key))
                {
                    throw new ArgumentException($"{this._type.FullName}.{kv.Key}值域范围已添加,不可重复添加");
                }

                this._valueRangeDic.Add(kv.Key, kv.Value);
            }
        }





        /// <summary>
        /// 移除一个值域
        /// </summary>
        /// <param name="memberName">属性或字段名称</param>
        /// <exception cref="ArgumentNullException"></exception>
        public void Remove(string memberName)
        {
            if (string.IsNullOrEmpty(memberName))
            {
                throw new ArgumentNullException(nameof(memberName));
            }

            this._valueRangeDic.Remove(memberName);
        }

        /// <summary>
        /// 清空已有的值域
        /// </summary>
        public void Clear()
        {
            this._valueRangeDic.Clear();
        }




        /// <summary>
        /// 尝试获取值域,获取成功返回true,获取失败返回false
        /// </summary>
        /// <param name="memberName">属性或字段名称</param>
        /// <param name="valueRange">值域</param>
        /// <returns>获取成功返回true,获取失败返回false</returns>
        /// <exception cref="ArgumentNullException">成员名称为null或空将抛出异常</exception>
        public bool TryGetValueRange(string memberName, out IValueRange valueRange)
        {
            if (string.IsNullOrEmpty(memberName))
            {
                throw new ArgumentNullException(nameof(memberName));
            }

            return this._valueRangeDic.TryGetValue(memberName, out valueRange);
        }

    }
}
